package roleProxy

import (
	"encoding/json"
	"errors"
	"gitee.com/kudingc/role_proxy/common"
	"gitee.com/kudingc/role_proxy/models"
	"gitee.com/kudingc/role_proxy/routers"
	"gitee.com/kudingc/role_proxy/routers/login"
	"gitee.com/kudingc/role_proxy/routers/role"
	"gitee.com/kudingc/role_proxy/routers/routerProxy"
	"gitee.com/kudingc/role_proxy/service"
	"gitee.com/kudingc/zh_logs/logging"
	"github.com/astaxie/beego/cache"
	_ "github.com/astaxie/beego/cache/redis"
	"github.com/astaxie/beego/orm"
	"github.com/casbin/casbin"
	xormadapter "github.com/casbin/xorm-adapter"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

type RoleProxy struct {
	AppServerAddr string // 服务对外暴露地址 可以是 :8080 也可以是 127.0.0.1:8080
	AppListerAddr string // 微服务注册地址 同上
	Mysql         struct {
		SqlConn string // MySQL连接字符串 root:123456@tcp(127.0.0.1:3306)/role_proxy?charset=utf8
	}
	Redis struct {
		RedisCollectionName string // redis连接名称
		RedisConn           string // redis连接地址
		RedisDbNum          int    // redis分区号
		RedisPassword       string // redis密码
	}
	Jwt struct {
		EncryptionKey       string // jwt加密密钥
		CheckTokenExpired   bool   // 检查token是否过期
		TokenExpirationTime int    // token过期时间 单位分钟
	}
	Captcha struct {
		CheckCaptcha bool // 开启验证码验证
	}
	Logger struct {
		LogSavePath   string             // 日志保存位置
		LogSaveModel  logging.Model      // 日志保存方式
		LogSaveName   string             // 日志保存文件名
		LogLevel      logging.Level      // 日志级别
		LogFormatter  logging.Format     // 日志格式化方式
		LogMaxBackUp  int                // 日志保存文件个数
		LogSplitModel logging.SplitModel // 日志分割模式 默认按时间分割
		LogSplitTime  time.Duration      // 日志分割时长，只有LogSplitModel为Date时才生效
		PrintStdout   bool               // 是否同步输出到控制台
	}
}

// Init 初始化配置文件 入参为日志对象（注意需要先将对象初始化后再传进来），本模块所有日志都将保存到该对象中
func (r *RoleProxy) Init(rbacFilePath string) (db orm.Ormer, redis cache.Cache, zhLogger logging.ZhLogger, err error) {
	common.ZhLogger = logging.ZhLogger{
		LogSavePath:  r.Logger.LogSavePath,
		LogSaveModel: r.Logger.LogSaveModel,
		LogSaveName:  r.Logger.LogSaveName,
		LogLevel:     r.Logger.LogLevel,
		LogFormatter: r.Logger.LogFormatter,
		LogMaxBackUp: r.Logger.LogMaxBackUp,
		LogSplitTime: r.Logger.LogSplitTime,
		PrintStdout:  r.Logger.PrintStdout,
	}
	common.ZhLogger.InitConfig()

	common.CheckTokenExpired = r.Jwt.CheckTokenExpired
	common.TokenExpirationTime = r.Jwt.TokenExpirationTime
	common.TokenEncryptionKey = r.Jwt.EncryptionKey
	common.CheckCaptcha = r.Captcha.CheckCaptcha

	// 要使用自己定义的数据库rbac_db,最后的true很重要.默认为false,使用缺省的数据库名casbin,不存在则创建
	common.Adapter = xormadapter.NewAdapter("mysql", r.Mysql.SqlConn, true)
	common.Enforcer = casbin.NewEnforcer(rbacFilePath, common.Adapter)
	// 从DB加载策略
	common.Enforcer.LoadPolicy()

	// 注册驱动
	orm.RegisterDriver("mysql", orm.DRMySQL)
	// 注册 model
	orm.RegisterModel(new(models.SysUser), new(models.SysMenu), new(models.SysMenuApi), new(models.SysRole), new(models.SysRoleMenu), new(models.SysUserRole), new(models.SysRegService))
	// 注册默认数据库
	err = orm.RegisterDataBase("default", "mysql", r.Mysql.SqlConn, 30, 30) //注册默认数据库
	if err != nil {
		common.ZhLogger.Error("mysql连接失败！")
		return
	}
	common.Db = orm.NewOrm()
	common.Db.Using("default")
	common.ZhLogger.Info("******************************************************************************")
	common.ZhLogger.Info("********************************mysql启动成功**********************************")
	common.ZhLogger.Info("******************************************************************************")

	// 设置配置参数
	config := orm.Params{
		"key":      r.Redis.RedisCollectionName,
		"conn":     r.Redis.RedisConn,
		"dbNum":    r.Redis.RedisDbNum,
		"password": r.Redis.RedisPassword,
	}
	configStr, err := json.Marshal(config)
	if err != nil {
		common.ZhLogger.Error("redis配置模型转换失败")
		return common.Db, nil, common.ZhLogger, err
	}
	common.RedisCache, err = cache.NewCache("redis", string(configStr))
	if err != nil {
		common.ZhLogger.Error("redis初始化失败")
		return common.Db, nil, common.ZhLogger, err
	}
	common.ZhLogger.Info("******************************************************************************")
	common.ZhLogger.Info("********************************redis启动成功**********************************")
	common.ZhLogger.Info("******************************************************************************")

	return common.Db, common.RedisCache, common.ZhLogger, err
}

// RegisterApi 注册api
// apiAddr api地址
// method  api请求method 例 get post
func (r *RoleProxy) RegisterApi(apiAddr string, method common.Method, params map[string]*common.Param, f func() (data interface{}, err error)) (err error) {
	if common.Db == nil || common.RedisCache == nil {
		return errors.New("请先进行初始化操作")
	}

	common.RegisteredApi[apiAddr] = common.ApiData{
		ApiAddr: apiAddr,
		Method:  method,
		F:       f,
		Params:  params,
	}
	return
}

// StartServer 启动服务
func (r *RoleProxy) StartServer() (err error) {
	if common.Db == nil || common.RedisCache == nil {
		return errors.New("请先进行初始化操作")
	}

	// 监听tcp连接
	go service.ListenAndServe(r.AppListerAddr)

	// 加载多个APP的路由配置
	routers.Include(login.Routers, role.Routers, routerProxy.Routers)
	// 初始化路由
	engine := routers.Init()
	err = engine.Run(r.AppServerAddr)
	if err != nil {
		common.ZhLogger.Error(err)
	}
	return
}
